# from matplotlib.patches import Wedge
# from matplotlib.transforms import Bbox
# from numpy.core.defchararray import encode, index, title
# from numpy.lib.twodim_base import mask_indices
import pymongo
import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
import seaborn as sns
import dataframe_image as dfi
import os
import requests
from pyecharts.charts import BMap
from pyecharts import options as opts
from pyecharts.render import make_snapshot
from snapshot_phantomjs import snapshot

BAIDU_AK = ''
'''
更换城市：
    1、先建立本地对应文件夹；
    2、修改 self.city = xx
    3、修改 db.dzdp_xx
    4、注意 citydicts 
'''
citydicts = {'gz':'广州', 'sz':'深圳','sh':'上海','bj':'北京','cd':'成都'}


class read_db_data:
    def __init__(self):
        self.data = {}
        self.city = 'sh'
        
        
    def readdata(self):
        city = self.city
        client = pymongo.MongoClient(host='localhost', port=27017)
        db = client.dzdp
        collection = db.dzdp_sh  # 更换城市必须 更新！！
        result = collection.find()
        counts = collection.count_documents({})
        print('源数据条数：', counts)
        # print(result)
        # 转换格式
        # data = {}
        shopname = []
        score=[]
        numcomments=[]
        avgprice=[]
        foodtype=[]
        busdistname=[]
        location=[]
        tastescore=[]
        envscore=[]
        servescore=[]
        for i in result:
            shopname.append(i.get('shop_name'))
            score.append(float(i.get('score')))
            numcomments.append(float(i.get('num_comments')))
            foodtype.append(i.get('food_type'))
            busdistname.append(i.get('business_district_name'))
            location.append(i.get('location'))
            if i.get('avg_price') == '':
                avgprice.append(0)
            else:
                avgprice.append(float(i.get('avg_price')))            
            if i.get('taste_score') == "null":
                tastescore.append(0)
            else:
                tastescore.append(float(i.get('taste_score')))
            if i.get('environment_score') == 'null':
                envscore.append(0)
            else:
                envscore.append(float(i.get('environment_score')))    
            if i.get('serve_score') == 'null':
                servescore.append(0)
            else:
                servescore.append(float(i.get('serve_score')))  
        self.data['Shopname'] = shopname
        self.data['Score'] = score
        self.data['Comments'] = numcomments
        self.data['Avgprice'] = avgprice
        self.data['Foodtype'] = foodtype
        self.data['Busdistname'] = busdistname
        self.data['Location'] = location
        self.data['Tastescore'] = tastescore
        self.data['Envscore'] = envscore
        self.data['Servescore'] = servescore
        return self.data, city

    def cleandata(self):
        data = self.data
        df = pd.DataFrame(data)
        # print(df['Score'] == 0)
        # df.drop(df['Score']==0)
        # 排序：按评分
        df = df.sort_values(by='Score', ascending=False)
        # 数据描述
        # print(df.describe())
        df.to_csv('./dzdpA/' + self.city + '/df.csv', encoding='utf-8-sig')
        # 存在两行评分为0
        # df = df[:-1].dropna(how='any')
        # print(df[:-1].dropna())
        zero_index = df.Score[df.Score == 0].index
        # print(zero_index)
        for i in zero_index:
            # print(i)
            df.drop(i, inplace=True)
        # print(df.describe())
        df.to_csv('./dzdpA/' + self.city + '/df_drop_zero_score.csv', encoding='utf-8-sig')
        return df
# top10
def top10(df, label):
    index = np.arange(1,11,1)
    df = df.sort_values(by= label, ascending=False)
    df_top10 = df[:10].set_index(index)
    df_top10 = df_top10.rename(columns={'Shopname':'店名','Comments':'评论数','Score':'评分','Avgprice':'人均消费','Foodtype':'菜系','Busdistname':'商圈','Location':'地址','Tastescore':'口味评分','Envscore':'环境评分','Servescore':'服务评分'})
    df_top10.to_csv('./dzdpA/' + city + '/' + label + '_Top10.csv', encoding='utf-8-sig')
    dfi.export(df_top10, './dzdpA/' + city + '/' + label + '_Top10.jpg')
    return df_top10
# 中心趋势
def central_tendency(df,key,city):
    # dfs = df[key]
    data = {key: [df.min(),df.max(),df.mean(),df.median(),stats.mode(df)[0][0]]}
    df_ct = pd.DataFrame(data, index=['min', 'max', 'mean', 'median', 'mode'])
    # print(df_ct)
    # df_ct.to_csv('./dzdpA/' + city + '/Score中心趋势.csv')
    dfi.export(df_ct, './dzdpA/' + city + '/' + key + '中心趋势.jpg')

def df_groupby(data, key, key2, name, name2, city):
    data_choice = data.loc[:,[key, 'Shopname','Score', 'Avgprice', key2]]
    data_choice.to_csv('./dzdpA/' + city + '/' + key + '_data.csv', encoding='utf-8-sig')
    dfgroup = data_choice.groupby(key).agg({'Shopname':'count','Score': lambda x: round(np.mean(x),2),
                    'Avgprice':lambda x: round(np.mean(x)),key2:'nunique'}).reset_index(level=None)
    dfgroup.rename(columns={key:name,'Shopname':'数量','Score':'平均评分','Avgprice':'人均消费', key2:name2},inplace=True)
    dfgroup.to_csv('./dzdpA/' + city + '/' + key + '_group.csv', encoding='utf-8-sig')
    dfft_top10 = dfgroup.sort_values(by='数量',ascending=False).set_index(np.arange(1,len(dfgroup)+1,1))[:10]
    dfft_top10.to_csv('./dzdpA/' + city + '/' +  key + '_Top10.csv', encoding='utf-8-sig')
    dfi.export(dfft_top10, './dzdpA/' + city + '/' +  key + '_Top10.jpg')
    return dfft_top10,name

# 注释
def autolabel(rects, ax, foodtype):
        """Attach a text label above each bar in *rects*, displaying its height."""
        i = 1
        for rect in rects:
            height = rect.get_height()
            ax.annotate('{}'.format(height), # 文本内容
                        xy=(rect.get_x() + rect.get_width() / 2, height), # 注释的点
                        xytext=(0, 3),  # 3 points vertical offset 
                        textcoords="offset points", # 文本坐标系  offset points:偏移点
                        ha='center', # 水平位置
                        va='bottom') # 垂直位置  
            ax.text(rect.get_x() + rect.get_width() / 2, height/2, foodtype[i], va='center', ha='center')
            i += 1
# 离散度/相关性
def dispersion(data1,data2,key,city):
    std_df = data1.std()
    var_df = data1.var()
    print('std: {}'.format(std_df))
    print('var:{}'.format(var_df))
    cov_sc = np.cov(data1, data2)
    pearson_sc = np.corrcoef(data1,data2)
    print('协方差：', cov_sc)
    print('相关系数：', pearson_sc)
    data = {std_df, var_df, cov_sc[0,1], pearson_sc[0][1]}
    df_data = pd.DataFrame(data, index=['std', 'var', 'cov', 'pearson'], columns=[key])
    dfi.export(df_data, './dzdpA/' + city + '/' + key + '离散度相关性.jpg')
    

# 散点图
def scatterfig(x, y, xlabel, ylabel, title, pathname,city):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(x, y, marker='.')
    ax.set_title(title)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    plt.savefig('./dzdpA/'+ city + '/' + pathname + '_scatter.jpg')

# 柱形图
def barfig(figsize,xdata,ydata,zdata,ft,xlabel,ylabel,title,label1,label2,pathname,city):
        fig, ax = plt.subplots(figsize=figsize)
        x_tickslabel = xdata
        y = ydata
        xticks_com = np.arange(len(x_tickslabel))
        rects_comm = ax.bar(xticks_com, ydata, color='cornflowerblue',alpha=0.8, label=label1)
        ax.set_xlabel(xlabel)
        ax.set_ylabel(ylabel)
        ax.set_xticks(xticks_com)
        ax.set_xticklabels(x_tickslabel, rotation= 30)
        ax.set_title(title)
        autolabel(rects_comm, ax, ft)

        ax2 = ax.twinx()
        y_c_score = zdata
        y = np.arange(1,6,1)
        ax2.plot(xticks_com, y_c_score, color='y', marker='.', label=label2)
        ax2.set_yticks(y)
        ax2.set_label('评分')
        for a,b in zip(xticks_com, y_c_score):
            ax2.text(a, b-0.1, b, ha='left', va='top')

        fig.legend(ncol=2, bbox_to_anchor=(0.9, 1))
        fig.tight_layout()
        plt.savefig('./dzdpA/' + city + '/' + pathname + '_bar.jpg')       

# 饼图
def piefig(data, key, title, city):
    fig, ax = plt.subplots()
    ax.pie(data.values(), labels=data.keys(), autopct='%.00f%%')
    ax.set_title(title)
    plt.savefig('./dzdpA/' + city + '/' + key + '_pie.jpg')
# 饼图2
def piefig2(data, key, city, name):
    fig, ax = plt.subplots(figsize=(18,16))
    data_pie = [x for x in data['数量']]
    # print('data_pie',data_pie)
    # labels1 = data.iloc[:,[0]].values
    # print(labels1)
    labels = data[name]
    # print(labels)
    recipe = []
    a = data['平均评分']
    b = data['人均消费']
    for i in range(len(a)):
        recipe.append('{}分 {}元'.format(a[i+1], b[i+1]))
    # print(recipe)
    wedges, texts, percents = ax.pie(data_pie, startangle=60, labels=labels, autopct='%.f%%', textprops={'fontsize':28,'fontfamily':'SimHei'})        
    bbox_pros = dict(boxstyle='square,pad=0.3',fc='w',ec='k',lw=0.7)
    kw = dict(arrowprops=dict(arrowstyle="<-"),bbox=bbox_pros,va='center')
    for i,p in enumerate(wedges):
        # print(i,p)
        ang = (p.theta2 - p.theta1)/2 + p.theta1
        # print('ang',ang)
        x = np.cos(np.deg2rad(ang))
        y = np.sin(np.deg2rad(ang))
        # print('x',x, 'y', y)
        ha = {-1:'right', 1:'left'}[int(np.sign(x))]
        connectionstyle= "angle,angleA=0,angleB={}".format(ang)
        kw["arrowprops"].update({"connectionstyle":connectionstyle})
        ax.annotate(recipe[i],xy=(x, y),xytext=(1.4*np.sign(x), 1.6*y),ha=ha,**kw,fontsize=18,weight='bold')
    fig.suptitle(key+'Top10',fontsize=28)
    plt.tight_layout()
    plt.savefig('./dzdpA/' + city + '/' + key + '_top10_pie.jpg')

# 直方图
def histmap(data, key, bin, step, city):
    """
    :data: 处理后的数据
    :key: 分析的 类别
    :bin: 步长
    :step: arange的间隔
    """
    # bin = 40
    # plt.subplot(1,1,1)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    a = data.min()
    b = data.max()
    bin_edge = np.arange(a,b,step)
    counts, bins = np.histogram(data, bin_edge)
    # print(counts)
    mode_left = bins[np.argmax(counts)] # np.argmax返回一个numpy数组中最大值的索引值
    mode_right = bins[np.argmax(counts)+1]
    mode_middle = (mode_left + mode_right) / 2
    # print(mode_left, mode_right)
    # print(mode_middle)
    
    plt.axvline(x=data.mean(), linewidth=1, color='red', label='mean')
    plt.axvline(x=data.median(), linewidth=1, color='green', label='median')
    plt.axvline(x=mode_middle, linewidth=1, color='blue', label='mode')
    plt.hist(data, bins=bin, alpha=0.6)
    # plt.xlabel('key')
    # plt.ylabel('数量')
    ax.set_xlabel(key)
    ax.set_ylabel('数量')
    plt.legend()
    plt.savefig('./dzdpA/' + city + '/' + key +  '_hist.jpg')

def score_analysis(city, df):
    dfs = df['Score']
    dfc = df['Comments']
    # top10
    index = np.arange(1,11)
    df_s10 = df[:10].set_index(index)
    df_s10 = df_s10.rename(columns={'Shopname':'店名','Score':'评分','Comments':'评论数','Avgprice':'人均消费','Foodtype':'菜系','Busdistname':'商区','Location':'地址','Tastescore':'口味评分','Envscore':'环境评分','Servescore':'服务评分'})
    # print(df_s10)
    df_s10.to_csv('./dzdpA/' + city + '/Score_Top10.csv', encoding='utf-8-sig')
    dfi.export(df_s10, './dzdpA/' + city + '/Score_Top10_score_table.jpg')
    # 柱形图、折线图
    x_label = df_s10['店名']
    y_label = df_s10['评分']
    x = np.arange(len(x_label))  # the label locations
    fig, ax1 = plt.subplots(figsize=(18,8))
    rects_score = ax1.bar(x, y_label, color='cornflowerblue',alpha=0.8, label='评分')
    ax1.set_ylabel('评分')
    ax1.set_title('评分前十的店')
    ax1.set_xticks(x)
    ax1.set_xticklabels(x_label, rotation= 30)
    ax1.set_title('评分前十名', loc='center')
    foodtype = df_s10['菜系']
    autolabel(rects_score, ax1, foodtype)

    ax2 = ax1.twinx()
    y_label_c = df_s10['评论数']
    y = np.arange(0,11000,1000)
    ax2.plot(x, y_label_c, color='r', label='评论数', marker='.')
    ax2.set_ylabel('评论数')
    ax2.set_yticks(y)
    for a, b in zip(x, y_label_c):
        ax2.text(a, b-260, b, ha='center', va='top')

    # ax1.legend(loc='upper right')
    # ax2.legend()
    fig.legend(ncol=2, bbox_to_anchor=(0.9,1), loc='upper right')
    fig.tight_layout() # 自动调整子图参数，使之填充整个图像区域。 
    # plt.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文，已修改matplotlibrc,无需加
    # plt.rcParams['axes.unicode_minus']= False   # 显示负号
    # plt.show()
    plt.savefig('./dzdpA/' + city + '/Score_Top10_bar.jpg')
    # 中心趋势
    central_tendency(dfs, 'Score',city)
    ## 直方图
    fig = plt.figure()
    ax = fig.add_subplot(111)
    plt.hist(dfs, bins=40, alpha=0.6)
    plt.xlabel('评分')
    plt.ylabel('数量')
    # plt.show()
    plt.savefig('./dzdpA/' + city + '/Socre_hist.jpg')
    ## 众数区间
    bin_edge = np.arange(3,5, 0.1)
    counts, bins = np.histogram(dfs, bin_edge)
    # print(counts, bins)
    mode_left = bins[np.argmax(counts)]
    mode_right = bins[np.argmax(counts)+1]
    mode_middle = (mode_left + mode_right) / 2
    print('mode range: %.2f， %.2f' % (mode_right, mode_left))
    print('median:%.2f' % dfs.median())
    print('mean: %.2f' % dfs.mean())
    ## 偏度
    score_skew = dfs.skew()
    print('skewness: %.4f' % score_skew)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    plt.axvline(x=dfs.mean(), linewidth=1, color='red', label='mean')
    plt.axvline(x=dfs.median(), linewidth=1, color='green', label='median')
    plt.axvline(x=mode_middle, linewidth=1, color='blue', label='mode')
    plt.legend()
    plt.hist(dfs, bins=40, alpha=0.6)
    plt.xlabel = '评分'
    plt.ylabel = '数量'
    plt.savefig('./dzdpA/' + city + '/Score_skew.jpg')
    # 离散度：方差，标准差
    dispersion(dfs, dfc, 'Score',city)

def comments_analysis(city, df):
    dfc = df['Comments']
    dfs = df['Score']
    # print(dfc)
    # print(dfc.std())
    # top10
    df_top10 = top10(df, 'Comments')
    ## top10柱形图
    barfig((15,6),df_top10['店名'],df_top10['评论数'],df_top10['评分'],df_top10['菜系'],'店名','评论数','评论数前十名','评论数','评分','Comments_Top10',city)          
    # 中心趋势
    central_tendency(dfc, 'Comments', city)
    # print(dfc.describe())
    # 离散度：方差、标准差
    # 相关性:协方差，相关系数
    dispersion(dfc, dfs, 'Commments', city)
    # 分布情况(散点图)：评分对应的评论数
    scatterfig(df['Score'], df['Comments'], '评分', '评论数', '评分-评论数', 'Comments-Score', city)
    # 解释：大部分店的评论数在5000条以下，评分集中在4.4-4.5分
    # 直方图
    histmap(dfc, 'Comments', 80, 500, city)

# 人均消费
def avgprice_analysis(city,df):
    dfap = df['Avgprice']
    dfs = df['Score']
    # 总体分布情况-直方图
    histmap(dfap, 'Avgprice', 80, 20, city)
    # top10
    df_top10 = top10(df, 'Avgprice')
    ## top10柱形图
    barfig((15,6),df_top10['店名'],df_top10['人均消费'],df_top10['评分'],df_top10['菜系'],'店名','人均消费','人均消费前十名','人均消费','评分','Avgprice_Top10',city)          
    # 中心趋势
    central_tendency(dfap, 'Avgprice', city)
    # 离散度：方差、标准差（不参考）
    # 相关性:协方差，相关系数
    dispersion(dfap, dfs, 'Avgprice', city)
    # 划分区间，计数
    def avgprice_bins(df):
        data = {'50元以下':0, '50-100元':0, '100-150元':0, '150-200元':0, '200-300元':0, '300-500元':0, '500元以上':0}
        data_percent = []
        for i in df:
            if i <=50:
                data['50元以下'] += 1
            elif i <=100:
                data['50-100元'] += 1
            elif i <= 150:
                data['100-150元'] += 1
            elif i <= 200:
                data['150-200元'] += 1
            elif i <= 300:
                data['200-300元'] += 1
            elif i <= 500:
                data['300-500元'] += 1
            else:
                data['500元以上'] += 1
        sum_data = sum(data.values())
        for i in data.values():
            data_percent.append(round(i / sum_data,2))
        # print(data_percent)
        # print(data)
        return  data 
    # print('data:',data)
    # 分布情况-饼图
    data= avgprice_bins(dfap)
    piefig(data, 'Avgprice', '人均消费区间情况',city)
    # 分布情况-散点图：评分对应的人均消费
    scatterfig(df['Score'], df['Avgprice'], '评分', '人均消费', '评分-人均消费', 'Avgprice-Score', city)
    # 解释：大部分店的人均消费在250条以下，评分集中在4.3-4.9分

# 菜系
def foodtype_analysis(city, df):
    # dfft = df['Foodtype']
    # dfs = df['Score']
    data, name= df_groupby(df, 'Foodtype', 'Busdistname', '菜系', '覆盖商区', city)
    piefig2(data, '菜系', city,name)

# 商区
def businessdistrict(city, df):
    data, name = df_groupby(df,'Busdistname','Foodtype', '商区', '菜系', city)
    # print(data)
    piefig2(data, '商区', city, name)

    bsd = df['Busdistname']
    # print(bsd)
    def getLatLngByName(name):
        url = 'http://api.map.baidu.com/geocoding/v3/?address=%s&output=json&ak=%s'
        response = requests.get(url % (name, BAIDU_AK))
        loc = response.json()['result']['location']
        return loc['lng'], loc['lat']

    
    def baidumap(data, city, key1):
        # city_dict = {'gz':'广州', 'sz':'深圳','sh':'上海','bj':'北京','cd':'成都'}
        city_dict = citydicts
        city_cn = city_dict[city]
        bsd_loc = {}
        for i in data:
            # print(i)
            if i in bsd_loc:
                bsd_loc[i][2] += 1
                # print(bsd_loc)
            else:
                # 百度地图查询无返回经纬度数据，需跳过
                try:
                    # print(city_cn + i)
                    loc = getLatLngByName(city_cn + i)
                    bsd_loc[i] = [*loc, 1]
                except:
                    continue 
        # print(bsd_loc)

        # savedir = './dzdpA/' + city
        # print(savedir)
        # center = [sum([value[0] for key,value in bsd_loc.items()])/len(bsd_loc),
        #         sum([value[1] for key, value in bsd_loc.items()])/len(bsd_loc)] 
        center = [np.median([value[0] for key,value in bsd_loc.items()]),
                    np.median([value[1] for key, value in bsd_loc.items()])]
        # print(center)
        map_ = (BMap().add_schema(
            baidu_ak = BAIDU_AK,
            center = center,
            zoom = 12,
            is_roam = True)
            )
        for key, value in bsd_loc.items():
            map_.add_coordinate(name=key, longitude=value[0], latitude=value[1])
            map_.add('',[[key, value[2]]], type_ = 'effectScatter', label_opts=opts.LabelOpts(is_show=False))
        # map_.render(os.path.join(savedir, '/'+key1+'美食商圈百度地图.html'))
        return map_
    # baidumap(bsd, city,'Busdistname')
    make_snapshot(snapshot,baidumap(bsd, city,'Busdistname').render('./dzdpA/'+city+'/BusdistnameBDMap.html'), './dzdpA/'+city+'/BusdistnameBDMap.gif')





if __name__ == "__main__":
    data = read_db_data()
    readdata, city = data.readdata()
    # city = data.readdata()[1]
    df = data.cleandata()
    score_analysis(city,df)
    comments_analysis(city, df)
    avgprice_analysis(city, df)
    foodtype_analysis(city,df)
    businessdistrict(city,df)

